package myJs.user.mission

import myJs.Implicits._
import myJs.Utils
import myJs.Utils._

import scala.scalajs.js.Dynamic.{global => g}
import myJs.myPkg._
import myJs.myPkg.bootstrap.Bootstrap.default._
import myJs.myPkg.jquery._
import myJs.tool.Pojo.{FilterContentData, sortedSofts}
import myJs.tool.Tool
import org.scalajs.dom.ext.Ajax
import org.scalajs.dom.{Event, _}
import scalatags.Text.{TypedTag, all}
import scalatags.Text.all._
import shared._
import myJs.tool.Tool._

import scala.collection.SeqMap
import scala.concurrent.{Future, Promise, TimeoutException}
import scala.scalajs.js
import scala.scalajs.js.{Date, JSON}
import scala.scalajs.js.annotation.{JSExport, JSExportTopLevel}
import scala.scalajs.js.JSConverters._
import org.scalajs.macrotaskexecutor.MacrotaskExecutor.Implicits._

/**
 * Created by yz on 2019/4/25
 */
@JSExportTopLevel("MissionManage")
object MissionManage {
  val formId = "form"
  val tableId = "table"
  val updateFormId = "updateForm"
  val updateModalId = "updateModal"

  @JSExport("init")
  def init = {

    initFilterContent()
    refreshSelect2
    initTable
    refreshTable()
    updateMissionSocket
    registerEvent
    updateFormBootStrapValidator

  }

  def registerEvent = {
    tableFilterEvent
    $(document.body).on("click", s".myEditShow", "", (y: Element) => {
      val id = $(y).dataId
      editShow(id)
    })
    $(document.body).on("click", s".myUpdateName", "", (y: Element) => {
      updateName
    })
    $(document.body).on("click", s".idsButton", "", (y: Element) => {
      deletes()
    })
  }

  def deletes(parentId: String = "") = {
    val ids = Tool.getIdsValue(parentId)
    val options = SwalOptions(
      title = "",
      text = messages("confirmToDeleteTheTask"),
      `type` = "warning",
      showCancelButton = true,
      showConfirmButton = true,
      confirmButtonClass = Tool.confirmButtonClass,
      confirmButtonText = messages("confirm"),
      closeOnConfirm = false,
      cancelButtonText = messages("cancel"),
      cancelButtonClass = Tool.cancelButtonClass
    )
    Swal.swal(options, () => {
      val data = js.Dictionary(
        "ids" -> ids.toJSArray
      )
      val url = g.jsRoutes.controllers.MissionController.deletes().url.toString
      Ajax.post(url = s"${
        url
      }", data = JSON.stringify(data),
        headers = Map("Content-Type" -> "application/json"), responseType = "blob").map {
        xhr =>
          refreshTable {
            () =>
              val options = SwalOptions(
                `type` = "success",
                title = messages("success"),
                text = s"${messages("deleteSuccessfully")}！"
              )
              Swal.swal(options)
              Tool.getIds(parentId)
          }
      }
    })
  }

  def updateName = {
    val formId = updateFormId
    val modalId = updateModalId
    val bv = $(s"#${formId}").bv
    bv.validate()
    val valid = bv.isValid().asInstanceOf[Boolean]
    if (valid) {
      val data = $(s"#${formId}").serialize()
      val index = layer.alert(Tool.myElement, Tool.layerOptions)
      val url = g.jsRoutes.controllers.MissionController.updateName().url.toString
      Ajax.post(url = s"${url}", data = data, headers = Map("Content-Type" -> "application/x-www-form-urlencoded")).map { xhr =>
        refreshTable { () =>
          layer.close(index)
          $(s"#${modalId}").modal("hide")
          bv.resetForm(true)
          val options = SwalOptions(
            `type` = "success",
            title = messages("success"),
            text = messages("modifyTaskNameSuccessfully")
          )
          Swal.swal(options)
        }
      }
    }
  }

  def editShow(id: String) = {
    val url = g.jsRoutes.controllers.MissionController.getMissionById().url.toString
    Ajax.get(url = s"${url.noCache}&missionId=${id}").map { xhr =>
      val dict = JSON.parse(xhr.responseText).toJDJAn.toMyJDS
      $("#updateForm input[name='orignalMissionName']").`val`(dict("missionName"))
      $("#updateForm input[name='missionId']").`val`(dict("id"))
      $("#updateModal").modal("show")
    }
  }

  def refreshSelect2 = {
    refreshState
    refreshKind
  }

  def refreshKind = {
    val inputName = "kind"
    val data = myJs.tool.Pojo.sortedSofts.map(_.nameStr)
    val array = (data).map { v =>
      js.Dictionary("text" -> Tool.getShowName(v), "id" -> v)
    }
    val options =Select2Options(data = array.toJSArray, multiple = true, placeholder = messages("clickToChoose"))
    Tool.getInputByName(inputName).select2(options)
  }

  def refreshState = {
    val inputName = "state"
    val data = Tool.stateMap.keyList
    val array = (data).map { v =>
      js.Dictionary("text" -> Tool.getStateShowName(v), "id" -> v)
    }
    val options = Select2Options(data = array.toJSArray, multiple = true, placeholder = messages("clickToChoose"))
    Tool.getInputByName(inputName).select2(options)
  }

  def tableFilterEvent = {
    $(document).on("click", (y: Element, e: Event) => {
      val trueY = e.target.asInstanceOf[Element]
      val b = $(trueY).hasClass("select2-selection__choice__remove") ||
        $(trueY).hasClass("select2-search__field") ||
        $(trueY).hasClass("filterContent") ||
        $(trueY).parents().hasClass("filterContent")
      if (!b) {
        $(".filterContent").mapElems(y => y).filter { y =>
          $(y).isVisible
        }.foreach { y =>
          Tool.filterContentValueRevert($(y))
          $(y).hide()
        }
      }
    })
    $(document.body).on("click", ".myOperate", "", (y: Element) => {
      filterContentShow(y)
    })
    $(document.body).on("click", ".mySortLi", "", (y: Element) => {
      val parentId = ""
      Tool.execSort(y, parentId, () => {
        refreshTable()
      })
    })
    $(document.body).on("click", ".myConfirm", "", (y: Element) => {
      val parentId = ""
      val map = Map(
        "kind" -> Tool.kindShowNameMap,
        "state" -> Tool.stateMap,
      )
      Tool.confirmFilter(y, parentId, () => {
        refreshTable()
      }, map)
    })
    $(document.body).on("click", ".myHide", "", (y: Element) => {
      val parentId = ""
      Tool.hideFilterContentWithRevert(y)
    })
    $(document.body).on("click", ".myRemove", "", (y: Element) => {
      val parentId = ""
      Tool.removeCondition(y, parentId, () => {
        refreshTable()
      })
    })
    $(document.body).on("click", ".myRemoveSort", "", (y: Element) => {
      val parentId = ""
      Tool.removeSort(y, parentId, () => {
        refreshTable()
      })
    })
    $(document.body).on("click", ".myYear", "", (y: Element) => {
      val parentId = ""
      Tool.hideFilterContent(y)
      val showYearTextJq = $(y).find(".text")
      val showYearText = showYearTextJq.text().trim
      val showYearStr = messages("showYear")
      val hideYearStr = messages("hideYear")
      val newShowYearText = showYearText match {
        case `showYearStr` => hideYearStr
        case `hideYearStr` => showYearStr
      }
      showYearTextJq.text(newShowYearText)
      refreshTable { () => {
      }
      }
    })
  }

  def filterContentShow(y: Element) = {
    val parentElem = Tool.getParentElem("")
    $(".filterContent").hide()
    val fieldName = Tool.getFilterDataFieldName(y)
    val contentId = s"${parentElem} #${fieldName}Content"
    val contentPaddingTop = $(".page-content").css("padding-top").replaceAll("px$", "").toInt
    val yTop = $(y).offset().top - $(".page-header").height() - contentPaddingTop + $(y).height() + 2
    val pageWidth = document.body.clientWidth
    val yTotalLeft = $(y).offset().left
    val leftWidth = $("#myLeft").width()
    val yLeft = yTotalLeft - leftWidth
    val width = 210
    val trueYLeft = if (yTotalLeft + width > pageWidth) pageWidth - leftWidth - 220 - 6 else yLeft - 3
    $(s"${contentId}").css("top", yTop.toInt).css("left", trueYLeft.toInt).width(width)
    $(s"${contentId}").show()
    Tool.filterContentFillBeforeValue($(s"${contentId}"))
  }

  def initFilterContent(tabId: String = "") = {
    val textSortDatas = List(
      FilterContentData("missionName", messages("taskName")),
    )
    val select2SortDatas = List(
      FilterContentData("kind", messages("softName"), List.empty),
      FilterContentData("state", messages("taskState"), List.empty),
    )
    val timeSortDatas = List(
      FilterContentData("startTime", messages("startTime")),
      FilterContentData("endTime", messages("endTime"))
    )
    Tool.initFilterContent(textSortDatas = textSortDatas, timeSortDatas = timeSortDatas,
      select2SortDatas = select2SortDatas,
      parentId = s"${tabId}", showShowYear = true)
  }

  @JSExport("delete")
  def delete(id: String) = {
    val options = SwalOptions(
      title = "",
      text = messages("deleteConfirm"),
      `type` = "warning",
      showCancelButton = true,
      showConfirmButton = true,
      confirmButtonClass = Tool.confirmButtonClass,
      confirmButtonText = messages("confirm"),
      closeOnConfirm = false,
      cancelButtonText = messages("cancel"),
      showLoaderOnConfirm = true,
      cancelButtonClass = Tool.cancelButtonClass
    )
    Swal.swal(options, () => {
      val url = g.jsRoutes.controllers.MissionController.deleteMissionById().url.toString
      Ajax.delete(url = s"${url}?missionId=${id}").map { xhr =>
        refreshTable { () =>
          val options = SwalOptions(
            `type` = "success",
            title = messages("success"),
            text = messages("deleteSuccessfully")
          )
          Swal.swal(options)
        }
      }
    })
  }

  @JSExport("viewLog")
  def viewLog(id: String) = {
    val url = g.jsRoutes.controllers.MissionController.getLogContent().url.toString
    Ajax.get(url = s"${url.noCache}&missionId=${id}", headers = Map("Content-Type" -> "application/json")).map { xhr =>
      val data = xhr.response
      val options = LayerOptions.`type`(1).title(s"<h4>${messages("viewLog")}</h4>").area(js.Array("900px", "600px")).
        skin("layui-layer-demo").closeBtn(1).anim(2).shadeClose(true).maxmin(true).
        content(s"<pre style='word-wrap: break-word' class='genome-pre'>${data}</pre>")
      layer.open(options)
    }
  }

  def updateMissionSocket = {
    val url = g.jsRoutes.controllers.MissionController.updateMissionSocket().url.toString
    val wsUri = s"ws://${window.location.host}${url}"
    if(window.location.protocol=="http"){
      webSocket(wsUri)
    }else{
      updateByHand
    }

  }

  def webSocket(wsUri: String) = {
    val websocket = new WebSocket(wsUri)
    websocket.onopen = (evt) =>
      websocket.send(JSON.stringify(js.Dictionary("info" -> "start")))
    websocket.onclose = (evt) =>
      println(s"ERROR:${evt.code},${evt.reason},${evt.wasClean}")
    websocket.onmessage = (evt) => {
      refreshTable()
    }
    websocket.onerror = (evt) => {
      updateByHand
      println(s"ERROR:${evt.toString}")
    }
  }

  def updateByHand = {
    js.timers.setInterval(3000) {
      refreshTable()
    }
  }

  def tableF = {
    val url = g.jsRoutes.controllers.MissionController.getAllMission().url.toString
    Ajax.get(url = s"${url.noCache}", headers = Map("Content-Type" -> "application/json")).map { xhr =>
      xhr.responseText.toJArJDJAn.toMyJDS
    }
  }

  def sortExec(data: JArJDS, queryMap: Map[String, js.Array[String]]) = {
    val sortOp = queryMap.get("sort").map(_.head)
    val sortData = sortOp.map { sort =>
      val ascSortData = sort match {
        case _ => data.sortBy(_ (sort))
      }
      queryMap("order").head match {
        case "desc" => ascSortData.reverse
        case "asc" => ascSortData
      }
    }.getOrElse(data)
    sortData
  }

  def refreshTable(f: () => js.Any = () => ()) = {
    val parentId = ""
    tableF.map { data =>
      val queryMap = Tool.getQueryMap(parentId)
      println(queryMap)
      val sortData = sortExec(data, queryMap)

      def loopFilter(sortData: JArJDS) = {

        val filterColumns = queryMap.removedAll(List("sort", "order")).filter(_._2.map(_.trim).nonEmpty).keyList

        def loop(acc: JArJDS, elems: List[String]): JArJDS = {
          elems match {
            case Nil => acc
            case y :: ys =>
              val searchV = queryMap(y)
              val vs = searchV
              val fieldName = y.split("\\.")(0)
              val name = y.split("\\.")(1)
              val jq = $(s"#${fieldName}Content").findInputByName(name)
              val curFilterData = if (jq.hasClass("myDatepicker")) {
                acc.filter { row =>
                  val dbDate = row(fieldName)
                  if (name == "startTime") {
                    Tool.timeBefore(vs.head, dbDate)
                  } else {
                    Tool.timeAfter(vs.head, dbDate)
                  }
                }
              } else if (jq.hasClass("select2MultiInput")) {
                acc.filter { row =>
                  vs.exists(x=>row(fieldName) == x)
                }
              } else {
                acc.filter { row =>
                  row(fieldName).contains(vs.head)
                }
              }
              loop(curFilterData, ys)
          }
        }

        loop(sortData, filterColumns)
      }

      val filterData = loopFilter(sortData)
      val columns = getColumns()
      $(s"#${tableId}").bootstrapTable("refreshOptions", TableOptions.columns(columns.toJSArray)).
        bootstrapTable("load", filterData)
      f()
    }

  }

  def tbFmt(columnName: String): js.Function = (v: js.Any, row: js.Dictionary[js.Any]) => {
    val showYearStr = messages("showYear")
    val startTimeShowYear = $("#startTimeContent .myYear span.text").text().trim != showYearStr
    val endTimeShowYear = $("#endTimeContent .myYear span.text").text().trim != showYearStr
    columnName match {
      case "missionName" => {
        if (row("state") == "success") {
          val viewUrl = g.jsRoutes.controllers.MissionController.resultDetailBefore().url.toString
          val viewStr = a(title := messages("viewDetails"), href := s"${viewUrl}?missionId=${row("id")}", cursor.pointer,
            v.toString
          )
          val editStr = a(title := messages("modify"), cursor.pointer, `class` := "myEditShow", dataId := s"${row("id")}",
            span(em(cls := "fa fa-edit"))
          )
          List(editStr, viewStr).mkString("&nbsp;")
        } else {
          span(v.toString)
        }
      }
      case "kind" => {
        Tool.getKindA(v.toString)
      }
      case "state" => {
        val state1 = if (List("success").contains(row("state"))) {
          span(cls := "label label-success", s"${messages("success")} ")
        } else if (List("running").contains(row("state"))) {
          span(messages("running"),
            img(cls := "runningImage", src := "/assets/images/running2.gif", width := 30, height := 20))
        } else if (List("wait", "preparing").contains(row("state"))) {
          span(
            span(cls := "label label-default", messages("waiting")),
            all.raw("&nbsp;"),
            img(cls := "runningImage", src := "/assets/images/running2.gif", width := 30, height := 20))
        } else {
          span(cls := "label label-danger", messages("error"))
        }
        state1.toString()
      }
      case "startTime" =>
        val date = new Date(v.toString)
        Tool.dateTimeFormat(date, startTimeShowYear)
      case "endTime" =>
        if (v.toString.nonBlank) {
          val date = new Date(v.toString)
          Tool.dateTimeFormat(date, endTimeShowYear)
        } else v
      case _ => v
    }
  }

  def expStyle(columnName: String): js.Function = (v: js.Any, row: js.Dictionary[Any]) => columnName match {
    case "missionIntro" => js.Dictionary("css" -> js.Dictionary(
      "overflow" -> "hidden", "white-space" -> "nowrap", "text-overflow" -> "ellipsis", "max-width" -> "11em",
      "width" -> "12em"
    ))
    case _ => v
  }

  lazy val operateColumn = js.Array(
    ColumnOptions.field("operate").title(messages("operations")).formatter(operateFmt).filterControl("clear")
  )

  @JSExport("operateFmt")
  def operateFmt: js.Function = {
    (v: js.Any, row: js.Dictionary[js.Any]) =>
      val deleteStr = a(
        title := messages("delete"),
        cursor.pointer,
        onclick := s"MissionManage.delete('" + row("id") + "')",
        target := "_blank",
        span(
          em(cls := "fa fa-close")
        )
      )

      val viewStr = a(title := messages("log"), onclick := s"MissionManage.viewLog('${row("id")}')", cursor.pointer,
        span(em(cls := "fa fa-file-text"))
      )
      val downloadUrl = g.jsRoutes.controllers.MissionController.downloadResult().url.toString
      val downloadStr = a(title := messages("download"), href := s"${downloadUrl}?missionId=${row("id")}", cursor.pointer,
        span(em(cls := "fa fa-download"))
      )

      val reRunUrl = g.jsRoutes.controllers.MissionController.reRunMissionBefore().url.toString
      val reRunStr = a(title := messages("rerunWithModificantion"), href := s"${reRunUrl}?missionId=${row("id")}", cursor.pointer,
        //        target := "_blank",
        span(em(cls := "fa fa-refresh"))
      )

      val state1 = if (List("success", "warning").contains(row("state"))) {
        List(downloadStr)
      } else List[TypedTag[String]]()

      val state2 = if (List("success", "error").contains(row("state"))) {
        List(viewStr, reRunStr)
      } else List[TypedTag[String]]()

      val rs = state1 ::: state2 ::: List(deleteStr)
      rs.mkString("&nbsp;")

  }

  val checkColumn = ColumnOptions.field("check").checkbox(true)

  def getColumns() = {
    val map = SeqMap("missionName" -> messages("taskName"), "kind" -> messages("softName"), "state" -> messages("taskState"),
      "startTime" -> messages("startTime"), "endTime" -> messages("endTime"))
    val columnNames = map.keyList
    js.Array(checkColumn) ++ columnNames.map { columnName =>
      val title = map.getOrElse(columnName, columnName)
      val fmt = tbFmt(columnName)
      val style = expStyle(columnName)
      val basicOptions = ColumnOptions.field(columnName).title(title).sortable(true).formatter(fmt).cellStyle(style)
      columnName match {
        case x if List("missionName", "kind", "state", "startTime", "endTime").contains(x) => basicOptions.
          sortable(false).operateable(true)
        case _ => basicOptions
      }
    }.concat(operateColumn)
  }

  def initTable = {
    val columns = getColumns()
    val options = TableOptions.columns(columns.toJSArray).searchOnButton(false).filterControl(false).
      locale(Tool.getBTLanguage)
    $(s"#${tableId}").bootstrapTable(options)
    Tool.dateInit
    Tool.bindEvt()
  }

  def getId = {
    g.id.toString
  }

  def updateFormBootStrapValidator = {
    val url = g.jsRoutes.controllers.MissionController.missionNameCheck().url.toString
    val dict = js.Dictionary(
      "feedbackIcons" -> js.Dictionary(
        "valid" -> "glyphicon glyphicon-ok",
        "invalid" -> "glyphicon glyphicon-remove",
        "validating" -> "glyphicon glyphicon-refresh",
      ),
      "fields" -> js.Dictionary(
        "missionName" -> {
          val info = messages("taskName")
          js.Dictionary(
            "validators" -> js.Dictionary(
              "notEmpty" -> js.Dictionary(
                "message" -> s"${messages("null")}"
              ),
              "remote" -> js.Dictionary(
                "message" -> s"${messages("exist")}",
                "url" -> url,
                "type" -> "POST",
                "delay" -> 1000,
              ),
            )
          )
        },
      )
    )
    g.$("#updateForm").bootstrapValidator(dict)

  }


}
